(define (divisible? n d)
  (= 0 (modulo n d)))

(define the-empty-stream '())
(define (stream-null? e) (null? e))

(define (force delayed-object)
  (delayed-object))

(define (stream-car stream) (car stream))

(define (stream-cdr stream) (force (cdr stream)))

(define (stream-ref s n)
  (if (= n 0)
      (stream-car s)
      (stream-ref (stream-cdr s) (- n 1))))
(define (stream-map proc s)
  (if (stream-null? s)
      the-empty-stream
      (cons (proc (stream-car s))
            (lambda () (stream-map proc (stream-cdr s))))))
(define (stream-for-each proc s)
  (if (stream-null? s)
      'done
      (begin (proc (stream-car s))
             (stream-for-each proc (stream-cdr s)))))

(define (stream-filter pred stream)
  (if (stream-null? stream)
      the-empty-stream
      (if (pred (stream-car stream))
          (cons (stream-car stream)
                (lambda ()
                  (stream-filter pred (stream-cdr stream))))
          (stream-filter pred (stream-cdr stream)))))


(define (integers-starting-from n)
  (cons n (lambda () (integers-starting-from (+ n 1)))))

(define integers (integers-starting-from 1))

(define (stream-enumerate-interval low high)
  (if (> low high)
      the-empty-stream
      (cons
       low
       (lambda () (stream-enumerate-interval (+ low 1) high)))))
(define (sieve stream)
  (cons
   (stream-car stream)
   (lambda ()
     (sieve (stream-filter
             (lambda (x)
               (not (divisible? x (stream-car stream))))
             (stream-cdr stream))))))

(define primes (sieve (integers-starting-from 2)))

(define t1 (print (stream-ref primes 500)))

(define (fibgen a b)
  (cons a (lambda () (fibgen b (+ a b)))))
(define fibs (fibgen 0 1))

(define (take strm n)
  (if (= n 0)
      '()
      (cons (stream-car strm)
            (take (stream-cdr strm) (- n 1)))))

(define t2 (print (take fibs 20)))
